import MsgStruct, { msgStrcutCount } from "../cfg/MsgStruct";
import XlsxDefPrev from "../cfg/XlsxDefPrev";
import { BaseTimer } from "../base/BaseTimer";
import { BufferAsset, Input, input, KeyCode, resources } from "cc";
import { connMgr } from "./ConnMgr";
import { debugMgr } from "./DebugMgr";
import { errorMgr } from "./ErrorMgr";
import { EventId, eventMgr } from "./EventMgr";
import { initMsg, MsgCoder, MsgVo } from "../cfg/Msg";
import { initXlsx, loadData, xlsxCount } from "../cfg/Xlsx";
import { keyMgr } from "./KeyMgr";
import { layerMgr, ViewLayer } from "./LayerMgr";
import { loginMod, LoginMod } from "../mod/login/LoginMod";
import { modMgr } from "./ModMgr";
import { playerMod } from "../mod/player/PlayerMod";
import { popupMgr } from "./PopupMgr";
import { Views } from "../cfg/XlsxDef";

/**
 * 加载管理
 */
export const loadMgr = _ as LoadMgr;
export class LoadMgr extends BaseTimer {

    private _startTime: number;
    private _prog: number;
    private _onDone: () => void;

    init() {
        (loadMgr as any) = this;
        XlsxDefPrev();
        initXlsx(kcoder);
        initMsg(kcoder);

        debugMgr.isDebug && input.on(Input.EventType.KEY_DOWN, e => {
            if (keyMgr.isCtrl && keyMgr.isShift && e.keyCode === KeyCode.HOME) {
                this.reloadData();
            }
        });
    }

    /**
     * 首次加载
     */
    loadFirst(): void {
        layerMgr.open(Views.LoadingView, _, ViewLayer.Window, async () => {
            this._startTime = Date.now();
            this.prog = 0;
            this.loadStr = "加载配置中";
            try {
                let asset = await new Promise<BufferAsset>((res, rej) => {
                    resources.load("data", BufferAsset, (count, total) => {
                        this.loadStr = `加载配置中: ${count}/${total}`;
                        this.prog = (count / total) * 0.5;
                    }, (err, asset) => {
                        if (err) {
                            let str = this.loadStr = "配置加载失败";
                            return rej(str + ": " + err.message);
                        }
                        res(asset);
                    });
                });

                await new Promise((res, rej) => {
                    this.loadStr = `加载配置中: 0/${xlsxCount}`;
                    try {
                        loadData(new Uint8Array(asset.buffer()), count => {
                            this.loadStr = `加载配置中: ${count}/${xlsxCount}`;
                            this.prog = 0.5 + (count / xlsxCount) * 0.75;
                            if (count == xlsxCount) {
                                res(null);
                            }
                        })
                        resources.release("data", BufferAsset);
                    } catch (err) {
                        let str = this.loadStr = "配置加载失败";
                        rej(str + ": " + err.message);
                    }
                });

                await new Promise((res, rej) => {
                    this.loadStr = `加载协议中: 0/${msgStrcutCount}`;
                    try {
                        MsgCoder.loadAll(MsgStruct, count => {
                            this.prog = 0.75 + count / msgStrcutCount;
                            this.loadStr = `加载协议中: ${count}/${msgStrcutCount}`;
                            if (count == msgStrcutCount) {
                                res(null);
                            }
                        })
                    } catch (err) {
                        let str = this.loadStr = "协议加载失败";
                        rej(str + ": " + err.message);
                    }
                });

                this._onDone = this.connGate;
                this.loadStr = "加载中";
                this.prog = 1;

            } catch (err) {
                errorMgr.error(err.message);
            }
        });
    }

    /**
     * 连接gate
     */
    private connGate(reconnCount = 0, seconds = 0) {
        if (reconnCount >= 5) {
            this.loadStr = `连接服务器失败!`;
            return;
        } else if (seconds > 0) {
            this.loadStr = `连接服务器失败,${seconds}秒后进行重连...(${reconnCount})`;
            this.timerOnce(this.connGate, 1, [reconnCount, --seconds]);
            return;
        }
        this.loadStr = "正在连接服务器...";
        eventMgr.once(EventId.gateConn, data => {
            if (data.event) {
                this.connGate(++reconnCount, 3);
                return;
            }
            modMgr.initMod(new LoginMod());
        });
        connMgr.connGate();
    }

    /** 加载内容 */
    set loadStr(v: string) {
        eventMgr.send(EventId.loadingStr, v);
    }

    /** 当前进度 */
    get prog() { return this._prog; }
    set prog(v: number) {
        this._prog = v;
        //至少给个短暂的加载时间
        let time = Date.now() - this._startTime;
        let prog = Math.min(v, time / 666);
        eventMgr.send(EventId.loadingProg, prog);

        if (prog >= 1) {
            this._onDone && this._onDone();
            this._onDone = null;
        } else if (v >= 1 && time <= 666) {
            this.callLater(this.refreshProg);
        }
    }

    refreshProg() {
        this.prog = this.prog;
    }

    /**
     * 进入游戏加载
     * @param data 
     */
    loadGame(data: MsgVo.login.JoinData) {
        layerMgr.open(Views.LoadingView, _, _, () => {
            this.prog = 0;
            this.loadStr = "加载游戏模块";
            this._onDone = () => {
                this.loadStr = `加载完成,准备进入游戏...`
                playerMod.onJoin(data);
                loginMod.tosReadyEnterMap();
            }
            modMgr.initGameMods((loaded, total) => {
                if (loaded < total) {
                    this.prog = loaded / total;
                    this.loadStr = `加载游戏模块(${loaded}/${total})`;
                } else {
                    this.prog = 1;
                    this.loadStr = `加载中...)`;
                }
            });
        });
    }


    /**
     * 重载配置
     */
    reloadData() {
        resources.load("data", BufferAsset, (err, asset) => {
            if (err) return popupMgr.showFloatText("重载配置失败");
            loadData(new Uint8Array(asset.buffer()), _, reloadDic => {
                eventMgr.send(EventId.reloadXlsx, reloadDic);
            });
            resources.release("data", BufferAsset);
            popupMgr.showFloatTextDebug("已重载配置");
        });
    }

}

declare global {
    interface EventData {
        [EventId.reloadXlsx]: { [name: string]: any },
        [EventId.loadingStr]: string,
        [EventId.loadingProg]: number,
    }
}